/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package serveur;


import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author scriptoff
 */




//  pose un probleme de serialization  == transient 



public class Chat implements Serializable  {

    
    
    private  ConcurrentHashMap<String,Canal> listeCanaux  ;
    private  ConcurrentHashMap<String,Utilisateur> utilisateurs ;


    public Chat() {
        this.listeCanaux = new ConcurrentHashMap<String, Canal>();
        this.utilisateurs = new ConcurrentHashMap<String, Utilisateur>();
    }

    public  void envoyerMessage ( TraitementClient tc , String argument , String message ) throws NullPointerException
    {
        if ( argument.charAt(0) == '#' )
        {
            String nomCanal = argument.substring(1, argument.length());
            Canal canal = unCanal (nomCanal) ;
            boolean present = canal.utilisateurPresent(tc.getUtilisateur());
            if (present) {
                CopyOnWriteArraySet<TraitementClient> clients = recupererTraitementsClientCanal(canal);
                for( TraitementClient client : clients )
                {
                    try {
                        client.envoyerMessageCanal( tc.getUtilisateur().getPseudo() , nomCanal, message);
                    } catch (Exception ex) {
                        new TraiteException(ex, "envoyerMessage");
                    }
                }
            }
        } else {
            Utilisateur util = unUtilisateur(argument) ;
            TraitementClient tcDestinataire = util.getTraitementClient() ;
            String pseudoEnvoyeur = tc.getUtilisateur().getPseudo() ;
            try {
                tc.envoyerMessageUtilisateur(pseudoEnvoyeur,util.getPseudo(), message);
                tcDestinataire.envoyerMessageUtilisateur(pseudoEnvoyeur, util.getPseudo() ,message);
            } catch (Exception ex) {
                new TraiteException(ex, "envoyerMessage");
            }
        }
    }

    public void inscriptionChat(TraitementClient tc, String pseudo , String password) 
    {
        
        Utilisateur utilisateur = unUtilisateur(pseudo);
        
        if ( utilisateur != null ){
            try {
                tc.envoyerErreurInscription();
            } catch (Exception ex) {
                new TraiteException(ex, "inscriptionChat");
            }
        } else {
            
            Utilisateur nouveauUtil = new Utilisateur ( pseudo , password , tc );
            ajouterUtilisateur(pseudo, nouveauUtil);
            try {
                tc.envoyerInscriptionOk();
            } catch (Exception ex) {
                new TraiteException(ex, "inscriptionChat");
            }
        }
    }

    public void connectionChat(TraitementClient tc, String pseudo, String password) throws NullPointerException {
        Utilisateur util = unUtilisateur(pseudo);
        if(util == null) {
            try {
                tc.envoyerErreurConnection();
            } catch (Exception ex) {
                new TraiteException(ex, "connectionChat");
            }
        } else {
            // C'est un object donc equals ( reférence sur un object )
            //  password != util.getPassword() equivalent a comparer deux pointeurs
            if(!util.getPassword().equals(password)) {
                 try {
                    tc.envoyerErreurConnection();
                } catch (Exception ex) {
                    new TraiteException(ex, "connectionChat");
                }
            } else {
                util.ajouterTraitementClient(tc);
                try {
                    tc.envoyerConnectionOk();
                } catch (Exception ex) {
                    new TraiteException(ex, "connectionChat");
                }
            }
        }
    }

    public  void listeCanauxChat(TraitementClient tc) {

        ConcurrentHashMap<String,Canal>  canaux = tesCanaux();
         try {
             tc.envoyerListeCanaux(canaux);
        } catch (Exception ex) {
            new TraiteException(ex, "listeCanauxChat");
        }

    }

    public void supprimerCanal( TraitementClient tc, String nomCanal) throws NullPointerException
    {
        Canal canal = unCanal (nomCanal) ;
        if ( !canal.estCreateur(tc.getUtilisateur()) )
        {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "supprimerCanal");
            }
        } else {
            envoyerMessage(tc, "#"+nomCanal, "Le canal a été supprimé");
            CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
            canal.supprimerCanal();
            supprimerCanal(nomCanal);
            for(TraitementClient tcCanal:tscDuCanal) {
                try {
                    tcCanal.envoyerCanalSupprime(nomCanal);
                } catch (Exception ex) {
                    new TraiteException(ex, "supprimerCanal");
                }
            }

        }
        
    }

    public  void bannirUtilisateur(TraitementClient tc, String nomCanal, String pseudoABannir) throws NullPointerException {
        if ( pseudoABannir.equals(tc.getUtilisateur().getPseudo()) )return ;
        Canal canal = unCanal(nomCanal);
        Utilisateur util = unUtilisateur(pseudoABannir);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        boolean estCreateur = canal.estCreateur(util);
        if(!autorise || estCreateur) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "bannirUtilisateur");
            }
        } else {
            envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+pseudoABannir+" a été banni");
            canal.bannirUtilisateur(util);
            try {
                tc.envoyerBannirUtilsateurOk();
             } catch (Exception ex) {
                new TraiteException(ex, "bannirUtilisateur");
            }
        }
    }

    public  void deBannirUtilisateur(TraitementClient tc, String nomCanal, String pseudoADeBannir) throws NullPointerException {
        Canal canal = unCanal(nomCanal);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        if(!autorise) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "deBannirUtilisateur");
            }
        } else {
            Utilisateur util = unUtilisateur(pseudoADeBannir);
            if ( util == null  )
                return ;
            envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+pseudoADeBannir+" a été débanni");
            canal.deBannirUtilisateur(util);
            try {
                tc.envoyerDeBannirUtilisateurOk();
            } catch (Exception ex) {
                new TraiteException(ex, "deBannirUtilisateur");
            }
        }
    }

    public  void expulserUtilisateur(TraitementClient tc, String nomCanal, String pseudoAExpulser) throws NullPointerException {
        if ( pseudoAExpulser.equals(tc.getUtilisateur().getPseudo()) )return ;
        Canal canal = unCanal(nomCanal);
        Utilisateur util = unUtilisateur(pseudoAExpulser);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        boolean utilisateurProtege = canal.utilisateurAutorise(util);
        if(!autorise || utilisateurProtege) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "expulserUtilisateur");
            }
        } else {
            envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+pseudoAExpulser+" a été expulsé");
            TraitementClient tcExpulser = util.getTraitementClient() ;
            try {
                tcExpulser.envoyerErreurUtilisateurExpulser(nomCanal);
            } catch (IOException ex) {
                new TraiteException(ex, "expulserUtilisateur");
            }
            canal.supprimerUtilisateur(util);

            CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
            CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
            for(TraitementClient tcCanal:tscDuCanal) {
                try {
                    tcCanal.raffraichirCanal(nomCanal, listeUtilisateurs);
                } catch (Exception ex) {
                    new TraiteException(ex, "expulserUtilisateur");
                }
            }
        }
    }

    public  void nouveauSuperUtilisateur(TraitementClient tc, String nomCanal, String pseudoDuNouveauSuperUtilisateur) throws NullPointerException {
        Canal canal = unCanal(nomCanal);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        if(!autorise) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "nouveauSuperUtilisateur");
            }
        } else {
            envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+pseudoDuNouveauSuperUtilisateur+" est devenu super utilisateur");
            Utilisateur util = unUtilisateur(pseudoDuNouveauSuperUtilisateur);
            canal.ajouterSuperUtilisateur(util);
            CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
            CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
            for(TraitementClient tcCanal:tscDuCanal) {
                try {
                    tcCanal.raffraichirCanal(nomCanal, listeUtilisateurs);
                } catch (Exception ex) {
                    new TraiteException(ex, "nouveauSuperUtilisateur");
                }
            }
        }
    }

    public  void supprimerSuperUtilisateur(TraitementClient tc, String nomCanal, String pseudoSuperUtilisateur) throws NullPointerException {
        Canal canal = unCanal(nomCanal);
        Utilisateur util = unUtilisateur(pseudoSuperUtilisateur);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        boolean estCreateur = canal.estCreateur(util);
        if(!autorise || estCreateur) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "bannirUtilisateur");
            }
        } else {
            envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+pseudoSuperUtilisateur+" n'est plus super utilisateur");
            canal.supprimerSuperUtilisateur(util);
            CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
            CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
            for(TraitementClient tcCanal:tscDuCanal) {
                try {
                    tcCanal.raffraichirCanal(nomCanal, listeUtilisateurs);
                } catch (Exception ex) {
                    new TraiteException(ex, "nouveauSuperUtilisateur");
                }
            }
        }
    }

    public  void deconnectionChat(TraitementClient tc) throws NullPointerException {
        Utilisateur util = tc.getUtilisateur();
        CopyOnWriteArraySet<Canal> canaux = util.tesCanaux();
        for(Canal canal:canaux) {
            canal.supprimerUtilisateur(util);
            CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
            CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
            for(TraitementClient tcCanal:tscDuCanal) {
                try {
                        tcCanal.raffraichirCanal(canal.getNomCanal(), listeUtilisateurs);
                } catch (Exception ex) {
                    new TraiteException(ex, "deconnectionChat");
                }
           }
        }
        util.nettoyerListeCanaux();
        util.supprimerTraitementClient();
        //envoyerMessage(tc, "#"+canal.getNomCanal(), "L'utilisateur "+util.getPseudo()+" s'est déconnecté");
    }

    public  void quitterCanal(TraitementClient tc, String nomCanal) throws NullPointerException {
        Canal canal = unCanal(nomCanal);
        Utilisateur util = tc.getUtilisateur();
        envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+util.getPseudo()+" a quitté le canal");
        canal.supprimerUtilisateur(util);
        CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
        CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
        for(TraitementClient tcCanal:tscDuCanal) {
            try {
                tcCanal.raffraichirCanal(nomCanal, listeUtilisateurs);
            } catch (Exception ex) {
                new TraiteException(ex, "quitterCanal");
            }
       }
    }
    
    public  void rejoindreCreerCanal( TraitementClient tc, String nomCanal, String password) throws NullPointerException
    {
        Canal canal = unCanal(nomCanal);
        Utilisateur util = tc.getUtilisateur();
        
        if ( canal == null )
        {
            canal  = new Canal(nomCanal, password, util) ;
            ajouterCanal (nomCanal , canal) ;
        }
        boolean interdit = canal.utilisateurInterdit(util) ;
        if (interdit)
        {
            try {
                tc.envoyerErreurUtilisateurBanni();
            } catch (Exception ex) {
                new TraiteException(ex, "rejoindreCreerCanal");
            }
        } else {
            if ( !password.equals(canal.getPassword()) )
            {
                try {
                    tc.envoyerErreurPassword();
                } catch (Exception ex) {
                    new TraiteException(ex, "rejoindreCreerCanal");
                }
            } else {
                canal.ajouterUtilisateur(util);
                CopyOnWriteArraySet<TraitementClient> tscDuCanal = recupererTraitementsClientCanal(canal);
                CopyOnWriteArraySet<String> listeUtilisateurs = recupererNomsUtilisateursCanal(canal);
                for(TraitementClient tcCanal:tscDuCanal) {
                    try {
                        tcCanal.raffraichirCanal(nomCanal, listeUtilisateurs);
                    } catch (Exception ex) {
                        new TraiteException(ex, "rejoindreCanal");
                    }
                }
                envoyerMessage(tc, "#"+nomCanal, "L'utilisateur "+util.getPseudo()+" a rejoint le canal");
            }
        }
    }

    public void modifierParametresUtilisateur(TraitementClient tc, String ancienPassword, String nouveauPassword ) throws NullPointerException {
        Utilisateur util = tc.getUtilisateur();
        if(!util.getPassword().equals(ancienPassword)) {
            try {
                tc.envoyerErreurPassword();
            } catch (Exception ex) {
                new TraiteException(ex, "modifierParametresUtilisateur");
            }
        } else {
            util.modifierParametres(nouveauPassword);
            try {
                tc.envoyerModifierParametresPersonnelsOk();
            } catch (Exception ex) {
                new TraiteException(ex, "modifierParametresUtilisateur");
            }
        }
    }

    public void modifierPasswordCanal(TraitementClient tc, String nomCanal, String password) throws NullPointerException {
        Canal canal = unCanal(nomCanal);
        boolean autorise = canal.utilisateurAutorise(tc.getUtilisateur());
        if(!autorise) {
            try {
                tc.envoyerErreurDroitsInsuffisants();
            } catch (Exception ex) {
                new TraiteException(ex, "modifierPasswordCanal");
            }
        } else {
            canal.modifierPassword(password);
            try {
                tc.envoyerModifierPasswordCanalOk();
            } catch (Exception ex) {
                new TraiteException(ex, "modifierPasswordCanal");
            }
        }
    }

    void dialoguer( TraitementClient tc , String pseudo) throws NullPointerException {
        Utilisateur utilRecoit = unUtilisateur(pseudo);
        TraitementClient tcRecoit = utilRecoit.getTraitementClient();
        try {
            tc.envoyerCommandeDialogue(pseudo);
            tcRecoit.envoyerCommandeDialogue(tc.getUtilisateur().getPseudo());
        } catch (IOException ex) {
            new TraiteException(ex, "dialoguer");
        }
        

    }

    private  void ajouterCanal(String nomCanal, Canal canal) {
        listeCanaux.put(nomCanal, canal);
    }

    private  void ajouterUtilisateur(String pseudo, Utilisateur nvutilisateur) {
        utilisateurs.put(pseudo, nvutilisateur);
    }

    private  void supprimerCanal(String nomCanal) {
        listeCanaux.remove(nomCanal);
    }

    private  Canal unCanal(String argument) {
        
        return listeCanaux.get(argument) ;
    }

    private  CopyOnWriteArraySet recupererTraitementsClientCanal(Canal canal) {
        CopyOnWriteArraySet<TraitementClient> set = new CopyOnWriteArraySet<TraitementClient>() ;
        CopyOnWriteArraySet<Utilisateur>  listeUtilisateurCanal = canal.getListeUtilisateur();
        for ( Utilisateur util : listeUtilisateurCanal ){
            set.add(util.getTraitementClient());
        }
        return set ;
    }

    private  CopyOnWriteArraySet recupererNomsUtilisateursCanal(Canal canal) {
        // @ devant un SU sinon rien
        CopyOnWriteArraySet<Utilisateur> listeUtils = canal.getListeUtilisateur();
        CopyOnWriteArraySet<Utilisateur> listeSU = canal.getListeSU();
        CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<String>();
        for(Utilisateur util:listeUtils) {
            if(listeSU.contains(util)){
                set.add("@" + util.getPseudo());
            } else {
                set.add(util.getPseudo());
            }
        }
        return set;
    }

    private  Utilisateur unUtilisateur(String argument) {
        Utilisateur util = utilisateurs.get(argument);
        return util ;      
    }

    private  ConcurrentHashMap<String,Canal> tesCanaux(){
        return listeCanaux;
    }
}

